trigger CaseNationalOperations on Case (before insert, before update) {

	// Trigger for National Operations Cases (NATO)
	
	// Load the Nato record types
	ID ioRecordType = RecordTypeHelper.GetRecordTypeID('Case', 'National Ops - Insertion Order');
	ID crRecordType = RecordTypeHelper.GetRecordTypeID('Case','National Ops - Change Request');	
	
	/* Updated on 1/16/2012 by plo, AD-21590, get parent case map */
	Set<Id> parentCaseIds = new Set<Id>();
	for(Case c : trigger.new)
	{
		if(c.ParentId != null)
		   parentCaseIds.add(c.ParentId);
	}
	
	Map<Id,Case> parentCaseMap = new Map<Id,Case>([SELECT Id, IO_Received_Date__c FROM Case WHERE Id IN :parentCaseIds]);
		
	    for(Case newCaseObject : Trigger.new) {   	
	    	// Verify the it's a Change Request or Insertion Order	
		    if ((ioRecordType == newCaseObject.RecordTypeId) || (crRecordType == newCaseObject.RecordTypeId)){
		    	
		    	/*
		    	   Updated on 1/16/2012 by plo, AD-21590
		    	   on insert: default IO_Received_Date__c to CreatedDate, taken care of in WF since CreatedDate is a system value, will stay blank in before insert
		    	   on update on ParentId field: 
		    	      - if attaching to a parent case, update IO_Received_Date__c to the parent case IO_Received_Date__c
		    	      - if detaching from a parent case, default to CreatedDate
		    	*/
		    	// on insertion order type only
	    		if(newCaseObject.RecordTypeId == ioRecordType)
	    		{	    			
	    		   if(trigger.isUpdate && newCaseObject.ParentId != trigger.oldMap.get(newCaseObject.Id).ParentId)
	    		   {
	    		   	// default value, include insert
	    			   DateTime ioDT = newCaseObject.CreatedDate;
	    			   
	    			   if(newCaseObject.ParentId != null && parentCaseMap.containskey(newCaseObject.ParentId))
	    			   {
	    			   	ioDT = parentCaseMap.get(newCaseObject.ParentId).IO_Received_Date__c;
	    			   }
	    			   
	    			   newCaseObject.IO_Received_Date__c = ioDT;
	    		   }
	    		}
		    	
		    	// Insertion Orders can move to billing states
		    	boolean billingStatesAllows = ioRecordType == newCaseObject.RecordTypeId;
		    	
		    	if(trigger.isUpdate){
			    	Case oldCaseObject = trigger.oldMap.get(newCaseObject.ID);
	
					// Check if record type is changing to CR
					if(oldCaseObject.RecordTypeId == ioRecordType && newCaseObject.RecordTypeId == crRecordType){
						if(NatoCaseStateTable.IsBillingState(newCaseObject.status))
							newCaseObject.addError('Insertion Orders cannot be changed to Change Requests if they are currently in a Billing status.');		    	
					}
								    				    	
			    	// Update the checkboxes if necessary
			    	if(oldCaseObject.Keyed_In_AF__c != newCaseObject.Keyed_In_AF__c)
			    		newCaseObject.Keyed_in_AF_Checkbox__c = (newCaseObject.Keyed_In_AF__c != null); 
	
			    	if(oldCaseObject.Future_Start_Date__c != newCaseObject.Future_Start_Date__c)
			    		newCaseObject.Future_Start__c = (newCaseObject.Future_Start_Date__c != null); 

			    	if(oldCaseObject.Released_Live_Date__c != newCaseObject.Released_Live_Date__c)
			    		newCaseObject.Released_Live__c = (newCaseObject.Released_Live_Date__c != null); 
			    	
			    	// Is there a state change?			    	 		    	
			    	if(oldCaseObject.status != newCaseObject.status){
			    		// Get a list of the possible next states
						Set<string> states = NatoCaseStateTable.GetPossibleStates(oldCaseObject.Status, oldCaseObject.Released_Live__c, billingStatesAllows, newCaseObject.Future_Start_Date__c, oldCaseObject.NATO_QC_Level__c,
						oldCaseObject.Nato_Rep_SS__c, oldCaseObject.Order_Entry__c, oldCaseObject.Ad_Production__c, oldCaseObject.Quality_Control__c, oldCaseObject.Nato_Rep_QC2__c, oldCaseObject.Billing__c);
						
						// Verify that the new state is valid	
						if(!states.contains(newCaseObject.status)){
							newCaseObject.status.addError('The case status cannot move from "' + oldCaseObject.Status + '" to "' + newCaseObject.status + '".');
						}   	
	
						// Validte the requirements for this state are fulfilled
						List<string> validationErrors = NatoCaseStateTable.ValidateStateRequirements(newCaseObject.status, newCaseObject);
						for(string error : validationErrors)
							newCaseObject.status.addError(error);
						
						// Set the owner and owner links
						NatoCaseStateTable.SetOwnerOnStateChage(newCaseObject.status, newCaseObject);
		
						// Update counters
						NatoCaseStateTable.SetCounterOnStateChage(newCaseObject.status, newCaseObject, oldCaseObject);					
			    	}
			    	
			    	// If the owner has changes, update the NATO 'case team' fields
			    	if(oldCaseObject.OwnerId != newCaseObject.OwnerId){
			    		NatoCaseStateTable.SetRepOnAssignmentChage(oldCaseObject, newCaseObject); 
			    	}
		    	}
		    	
		    	if(trigger.isInsert){
		    		// Get a list of possible states	    
					Set<string> states = NatoCaseStateTable.GetPossibleStates(null, false, billingStatesAllows, newCaseObject.Future_Start_Date__c, 1,
											newCaseObject.Nato_Rep_SS__c, newCaseObject.Order_Entry__c, newCaseObject.Ad_Production__c, newCaseObject.Quality_Control__c, newCaseObject.Nato_Rep_QC2__c, newCaseObject.Billing__c);
													
					// Verify that the current states is valid											
					if(!states.contains(newCaseObject.status)){
						newCaseObject.status.addError('The inital case status cannot be "' + newCaseObject.status + '". Try one of these ' + states + '.');
					} 

					// Verify that all the requirements are met to move to this state
					List<string> validationErrors = NatoCaseStateTable.ValidateStateRequirements(newCaseObject.status, newCaseObject);
					for(string error : validationErrors)
						newCaseObject.status.addError(error);
														
					// Update the checkboxes if necessary
		    		newCaseObject.Keyed_in_AF_Checkbox__c = (newCaseObject.Keyed_In_AF__c != null); 
		    		newCaseObject.Future_Start__c = (newCaseObject.Future_Start_Date__c != null); 
		    		newCaseObject.Released_Live__c = (newCaseObject.Released_Live_Date__c != null); 
										
					// Set the owner and owner links
					NatoCaseStateTable.SetOwnerOnStateChage(newCaseObject.status, newCaseObject);

					// Update counters
					NatoCaseStateTable.SetCounterOnStateChage(newCaseObject.status, newCaseObject, null);					
		    	}
	    	}
	    	
	    }
}